package StratelegoGame;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import javax.swing.event.MouseInputListener;

/**
 * Class the represents the actual 2D
 * stratego game piece.
 * @author Jace Ferguson
 * @filename GameBoard2DPiece.java
 *
 */
public class GameBoard2DPiece extends JPanel implements GameBoardPiece, MouseInputListener{
	protected static final int FRIEND = 0;
	protected static final int FOE = 1;
	protected Image origImage;
	protected Image scaledImage;
	protected Integer value;
	protected Color color;
	protected Dimension pieceSize;
	protected GameBoard2DSpace space;
	protected Integer forwardMoveRestriction;
	protected Integer backwardMoveRestriction;
	protected Integer rightMoveRestriction;
	protected Integer leftMoveRestriction;
	protected boolean selectable = true;
	protected MouseEvent firstEvent = null;
	protected GameBoard2DLogic observer;
	protected String name;
	protected JLabel label;
	protected Integer type;
	protected int status;

	/**
	 * Constructor for the piece.
	 * @param value	Integer value of the piece.
	 * @param type	Int 	friend or foe.
	 * @param name	String 	a string to represent the piece character name
	 * @param imageFile	String	location to the imagefile for the piece
	 * @param color	Color	color to display the piece as.
	 * @throws IOException
	 */
	public GameBoard2DPiece(Integer value, int type, String name, String imageFile, Color color) throws IOException
	{
		super();
		this.name = name;
		this.color = color;
		//The jlabel shows the value of the piece.
		this.label = new JLabel();
		this.label.setText(value.toString());
		this.setupMouseListener();
		this.value = value;
		//Arbitrary piece size.
		this.pieceSize = new Dimension(50, 50);
		this.setBackgroundImage(imageFile);
		if(type != GameBoard2DPiece.FOE && type != GameBoard2DPiece.FRIEND)
		{
			System.err.println("A bad piece was made. It may not work.");
		}
		else
		{
			this.type = type;
		}
		this.setup();
	}

	/**
	 * Clear the space the piece is on.
	 */
	public void clearSpace()
	{
		this.space = null;
	}

	/**
	 * Create a scaled image of the piece depending on the size
	 * of the piece.
	 */
	private void createScaledImage()
	{
		if(this.origImage != null)
		{
			this.scaledImage = this.origImage.getScaledInstance(this.pieceSize.width, this.pieceSize.height, Image.SCALE_SMOOTH);
		}
	}

	/**
	 * Get the number of spaces a piece can move in reverse.
	 * @return	Integer
	 */
	public Integer getBackwardMoveRestriction()
	{
		return this.backwardMoveRestriction;
	}

	/**
	 * Get the number of spaces a piece can move forward.
	 * @return
	 */
	public Integer getForwardMoveRestriction()
	{
		return this.forwardMoveRestriction;
	}
	
	/**
	 * Get the label on the piece.
	 * @return	JLabel
	 */
	public JLabel getLabel()
	{
		return this.label;
	}
	
	/**
	 * Get the number of spaces a piece can move left.
	 * @return	Integer
	 */
	public Integer getLeftMoveRestriction()
	{
		return this.leftMoveRestriction;
	}
	
	/**
	 * Get the name of the piece
	 * @return	String
	 */
	public String getName()
	{
		return this.name;
	}
	
	/**
	 * Get the logic observer
	 * @return	GameBoard2DLogic
	 */
	public GameBoard2DLogic getObserver()
	{
		return this.observer;
	}
	
	/**
	 * Get whether or not the piece is selectable.
	 */
	public boolean getPieceSelectable()
	{
		return this.selectable;
	}

	/**
	 * Get the number of spaces the piece is allowed
	 * to move to the right.
	 * @return	Integer
	 */
	public Integer getRightMoveRestriction()
	{
		return this.rightMoveRestriction;
	}
	
	/**
	 * Get the space the piece is currently located on
	 * or null if it is not on a space.
	 * @return	GameBoard2DSpace | null
	 */
	public GameBoard2DSpace getSpace()
	{
		return this.space;
	}
	
	/**
	 * Get whether the piece is a friend or foe.
	 * @return	Integer
	 */
	public Integer getType()
	{
		return this.type;
	}
	
	/**
	 * Get the value for the piece
	 * @return	Integer
	 */
	public Integer getValue()
	{
		return this.value;
	}
	
	/**
	 * Returns if the piece is an enemy to the
	 * piece passed in. In other words returns
	 * true if the pieces have opposite types.
	 * @param piece	GameBoard2DPiece
	 * @return	Boolean
	 */
	public boolean isFoe(GameBoard2DPiece piece)
	{
		if(this.type != piece.getType())
		{
			return true;
		}
		return false;
	}
	
	/**
	 * Returns if the piece is a friend to the 
	 * piece passed in. In other words returns
	 * true if the pieces have the same type.
	 * @param piece	GameBoard2DPiece
	 * @return	Boolean
	 */
	public boolean isFriend(GameBoard2DPiece piece)
	{
		if(this.type == piece.getType())
		{
			return true;
		}
		return false;
	}
	
	/**
	 * Detects when the piece is clicked.
	 * If the piece is selectable it notifies the 
	 * logic observer that the piece was clicked.
	 */
	public void mouseClicked(MouseEvent e)
	{
		if(this.selectable)
		{
			this.notifyObserver(e);
		}
	}
	
	/**
	 * Detects when the mouse is moved for DND 
	 * events.
	 */
	public void mouseDragged(MouseEvent e)
	{
		if(this.firstEvent != null)
		{
			e.consume();

			int action = TransferHandler.MOVE;

			int dx = Math.abs(e.getX() - firstEvent.getX());
			int dy = Math.abs(e.getY() - firstEvent.getY());
			//Arbitrarily define a 5-pixel shift as the
			//official beginning of a drag.
			if (dx > 5 || dy > 5) {
				//This is a drag, not a click.
				JComponent c = (JComponent)e.getSource();
				TransferHandler handler = c.getTransferHandler();
				if(handler != null) //If there isn't a handler set, don't attempt to DND.
				{
					//Tell the transfer handler to initiate the drag.
					handler.exportAsDrag(c, firstEvent, action);

				}
				firstEvent = null;
			}
		}
	}
	public void mouseEntered(MouseEvent e){}
	public void mouseExited(MouseEvent e){}
	public void mouseMoved(MouseEvent e){}
	
	/**
	 * Mark where the piece started for DND.
	 */
	public void mousePressed(MouseEvent e){
		this.firstEvent = e;
		e.consume();
	}
	
	/**
	 * Clear the start movement.
	 */
	public void mouseReleased(MouseEvent e){
		this.firstEvent = null;
	}
	
	/**
	 * Notify the logic that a mouseEvent occurred.
	 * @param e	MouseEvent	
	 */
	public void notifyObserver(MouseEvent e)
	{
		if(this.observer != null)
		{
			this.observer.notify(e);
		}
	}
	
	/**
	 * Paint the piece with the image or a color.
	 */
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		Graphics gr = g.create();
		
		//If there is a scaled image set it as the piece.
		if(this.scaledImage != null)
		{
			gr.drawImage(this.scaledImage, 0, 0, this);
		}
		else //Draw the image with just the color.
		{
			gr.setColor(this.color);
			gr.fillOval(0, 0, this.pieceSize.width, this.pieceSize.height);
		}
		
		//Reset the color back to black.
		gr.setColor(Color.BLACK);
	}
	
	/**
	 * Set the background image file for the piece.
	 * @param imageFile	String path to the image
	 * @throws IOException
	 */
	public void setBackgroundImage(String imageFile) throws IOException
	{
		if(imageFile != null && imageFile instanceof String)
		{
			this.origImage = ImageIO.read(new File(imageFile));
			//Create a new scaled image
			this.createScaledImage();
			//Repaint the component with the new image.
			this.repaint();
		}
	}
	
	/**
	 * Set the number of spaces a piece can move backwards.
	 * @param	limit 	Integer
	 */
	public void setBackwardMoveRestriction(Integer limit)
	{
		this.backwardMoveRestriction = limit;
	}
	
	/**
	 * Set the number of spaces a piece can move forwards.
	 * @param 	limit	Integer
	 */
	public void setForwardMoveRestriction(Integer limit)
	{
		this.forwardMoveRestriction = limit;
	}
	
	/**
	 * Set the number of spaces a piece can move to the left.
	 * @param	limit 	Integer
	 */
	public void setLeftMoveRestriction(Integer limit)
	{
		this.leftMoveRestriction = limit;
	}
	
	/**
	 * Set the number of spaces the piece can move in the 
	 * for directions.
	 * @param	forward	Integer
	 * @param	backward	Integer
	 * @param	right	Integer
	 * @param	left	Integer
	 */
	public void setMoveRestrictions(Integer forward, Integer backward, Integer right, Integer left)
	{
		this.forwardMoveRestriction = forward;
		this.backwardMoveRestriction = backward;
		this.rightMoveRestriction = right;
		this.leftMoveRestriction = left;
	}
	
	/**
	 * Set the logic observer for the piece.
	 * @param observer	GameBoard2DLogic
	 */
	public void setObserver(GameBoard2DLogic observer)
	{
		this.observer = observer;
	}
	
	/**
	 * Set whether the piece is selectable or not.
	 * @param	boolean selectable
	 */
	public void setPieceSelectable(boolean selectable)
	{
		this.selectable = selectable;
	}
	
	/**
	 * Update the size of the piece
	 * @param	size	Dimension 	new size.	
	 */
	public void setPieceSize(Dimension size)
	{
		this.pieceSize = size;
		this.updateSize();
	}
	
	/**
	 * Set the number of spaces the piece can move right.
	 * @param	limit	Integer	
	 */
	public void setRightMoveRestriction(Integer limit)
	{
		this.rightMoveRestriction = limit;
	}
	
	/**
	 * Set the piece's space when it gets added to a new space.
	 */
	public void setSpace(GameBoardSpace space)
	{
		this.space = (GameBoard2DSpace)space;
	}
	
	/**
	 * Set the type of the piece to Friend or Foe.
	 * @param type	Integer
	 */
	public void setType(Integer type)
	{
		this.type = type;
	}
	
	/**
	 * Setup the piece with handlers and a label.
	 */
	public void setup()
	{
		//Make the piece transparent
		this.setOpaque(false);
		//Clear the DND handler by default
		this.setTransferHandler(null);
		
		//Create the label that displays the value
		this.label = new JLabel();
		this.label.setForeground(Color.WHITE);
		this.label.setText(this.value.toString());
		
		//Add the label to the piece.
		this.add(this.label);		
	}
	
	/**
	 * Add the required mouse listeners and
	 * set the logic observer.
	 */
	public void setupMouseListener()
	{
		this.addMouseMotionListener(this);
		this.addMouseListener(this);
		this.setObserver(GameBoard2DLogic.getInstance());
	}
	
	/**
	 * Set the size of the piece and update the image.
	 */
	public void updateSize()
	{
		this.setMaximumSize(this.pieceSize);
		this.setPreferredSize(this.pieceSize);
		this.createScaledImage();
		this.repaint();
	}

}


